Introduction

The data were obtained in a portuguese survey of student enrolled in a portuguese language course in secondary school. The following features were recorded:

  1. school - student’s school (binary: ‘GP’ - Gabriel Pereira or ‘MS’ - Mousinho da Silveira)
  2. sex - student’s sex (binary: ‘F’ - female or ‘M’ - male)
  3. age - student’s age (numeric: from 15 to 22)
  4. address - student’s home address type (binary: ‘U’ - urban or ‘R’ - rural)
  5. famsize - family size (binary: ‘LE3’ - less or equal to 3 or ‘GT3’ - greater than 3)
  6. Pstatus - parent’s cohabitation status (binary: ‘T’ - living together or ‘A’ - apart)
  7.  Medu - mother’s education (numeric: 0 - none, 1 - primary education (4th grade), 2 – 5th to 9th grade, 3 – secondary education or 4 – higher education)
  8. Fedu - father’s education (numeric: 0 - none, 1 - primary education (4th grade), 2 – 5th to 9th grade, 3 – secondary education or 4 – higher education)
  9. Mjob - mother’s job (nominal: ‘teacher’, ‘health’ care related, civil ‘services’ (e.g. administrative or police), ‘at_home’ or ‘other’)
  10. Fjob - father’s job (nominal: ‘teacher’, ‘health’ care related, civil ‘services’ (e.g. administrative or police), ‘at_home’ or ‘other’)
  11. reason - reason to choose this school (nominal: close to ‘home’, school ‘reputation’, ‘course’ preference or ‘other’)
  12. guardian - student’s guardian (nominal: ‘mother’, ‘father’ or ‘other’)
  13. traveltime - home to school travel time (numeric: 1 - 1 hour)
  14. studytime - weekly study time (numeric: 1 - 10 hours)
  15. failures - number of past class failures (numeric: n if 1<=n<3, else 4)
  16. schoolsup - extra educational support (binary: yes or no)
  17. famsup - family educational support (binary: yes or no)
  18. paid - extra paid classes within the course subject (Math or Portuguese) (binary: yes or no)
  19. activities - extra-curricular activities (binary: yes or no)
  20. nursery - attended nursery school (binary: yes or no)
  21. higher - wants to take higher education (binary: yes or no)
  22. internet - Internet access at home (binary: yes or no)
  23. romantic - with a romantic relationship (binary: yes or no)
  24. famrel - quality of family relationships (numeric: from 1 - very bad to 5 - excellent)
  25. freetime - free time after school (numeric: from 1 - very low to 5 - very high)
  26. goout - going out with friends (numeric: from 1 - very low to 5 - very high)
  27. Dalc - workday alcohol consumption (numeric: from 1 - very low to 5 - very high)
  28. Walc - weekend alcohol consumption (numeric: from 1 - very low to 5 - very high)
  29. health - current health status (numeric: from 1 - very bad to 5 - very good)
  30. absences - number of school absences (numeric: from 0 to 93)
library(tidyverse)
library(reshape2)
library(fastDummies)
library(FactoMineR)
library(factoextra)
library(gridExtra)
library(psych)

# Load dataset.
alcohol <- read.csv("student-por.csv", stringsAsFactors = TRUE)
rmarkdown::paged_table(alcohol)

Correlation matrix

We are interested in the correlation between the features. To do this, variables must be dummy coded, i.e. new binary variables are created to encode nominal variables.

# Dummy code variables.
alcohol_dummy_coded <- dummy_columns(
  alcohol,
  remove_first_dummy = TRUE,
  remove_selected_columns = TRUE
)

The correlation matrix can then be computed.

# Plot correlation matrix.
alcohol_dummy_coded %>% 
  cor %>%
  melt %>%
  ggplot(aes(x = Var1, y = Var2, fill = value)) + 
    geom_tile(color = "grey") + 
    scale_fill_gradient2(
      low = "blue", high = "red", mid = "white",
      midpoint = 0, limit = c(-1,1), name = "Pearson\nCorrelation"
    ) + 
    theme(
      axis.text.x = element_text(angle = 90, size = 5, hjust = 1, vjust = 0),
      axis.text.y = element_text(size = 5, hjust = 1, vjust = 0),
      legend.title = element_text(size = 7), 
      legend.text = element_text(size = 5)
    ) +
    guides(fill = guide_colourbar(barwidth = 0.5, barheight = 5)) +
    coord_fixed() +
    xlab("") +
    ylab("") 

Unsurprisingly, grades G1, G2 and G3 are strongly positively correlated. Since including nearly-redundant variables can cause methods like PCA to overemphasize their contribution (i.e. several eigenvectors will be more weighted, thereby changing the directions of all eigenvectors), we choose to keep only the mean of these grades.

# Compute mean of grades G1, G2 and G3.
alcohol <- alcohol %>%
  mutate(mean_grade = (G1 + G2 + G3)/3) %>%
  select(-c(G1, G2, G3))

PCA on quantitative variables

Since we don’t have access to an expert opinion on this dataset, we cannot determine the number of factors in advance. Thus, it makes more sense to use principal component analysis over factor analysis. PCA can be applied on all numeric and ordinal variables if there is sufficient correlation between them (PCA on a set of orthogonal variables would lead to the same set of variables). Since the previous correlation matrix clearly isn’t the identity matrix (concerned readers could run a Bartlett test of homogeneity of variances to be assured of this), PCA can be applied.

alcohol_numeric_scaled <- alcohol %>%
  select_if(is.numeric) %>%
  scale

pca_res <- PCA(alcohol_numeric_scaled, 
               ncp = 10, 
               scale.unit = FALSE,
               graph = FALSE)

fviz_eig(pca_res)

The previous screeplot shows that we couldn’t find very strong components. Since there isn’t any sharp break in the screeplot, we use Horn’s parallel analysis to determine the number of components to keep.

fa.parallel(alcohol %>% select_if(is.numeric), fa = "pc")
Parallel analysis suggests that the number of factors =  NA  and the number of components =  4 

Analysis of variables

Let’s look at the first five principal components and try to interpret them in relation to the survey variables:

plots <- list()
for (i in 1:5) {
  pca_cor_df <- data.frame(
    label = alcohol %>% select_if(is.numeric) %>% names,
    correlation = pca_res$var$cor[, i]
  )
  plots[[i]] <- pca_cor_df %>% 
    ggplot(aes(x = label, y = correlation)) +
      geom_bar(stat = "identity", fill = "steelblue") +
      coord_flip() + 
      theme(axis.text.y = element_text(size = 12))
}

plots[[1]]

Seeing that the first principal component shows high positive correlation with features like alcohol consumption, number of class failures and frequency of time spent out with friend and high negative correlation with the mean grade, time spent studying and parents’ education, it could represent the school failures caused by alcohol consumption, lack of studying and/or parent’s lack of education.

plots[[2]]

The second component is highly correlated with the parents’ education and relatively correlated with alcohol consumption and time spent out with friend, while being slightly anti-correlated with travel time. It could represent the social status, since students whose parents received better education are more likely to belong to higher social classes.

plots[[3]]

The third component is highly correlated with free time and family relationship and anti-correlated with the student’s absences. It could represent the student’s family relationship, as students who have good family relationship are likely to be given more free time and not skip school.

plots[[4]]

The fourth component could represent the class failures that aren’t caused by alcohol consumption or lack of studying, as opposed to the first component. However, the numerical and ordinal data doesn’t contain variables that explain these failures.

plots[[5]]

The fifth component is correlated with age and study time and anti-correlated with health. It is harder to represent, but could roughly represent older students who might be exhausted from studying, or might not be happy with their environment due to their older age.

Analysis of individuals

To deepen our understanding of the previously studied components, let’s look at the most contributing individuals of each component.

# Find the ten most contributing individuals to PC n.
MostContribInd <- function(dim, n = 10) {
  dim_name <- enexpr(dim)

  ind <- pca_res$ind$contrib %>% 
    data.frame %>% 
    select(!!dim_name) %>% 
    arrange(desc(!!dim_name)) %>% 
    head(n = n) %>%
    rownames %>%
    as.numeric

  return(alcohol[ind, ])
}

rmarkdown::paged_table(MostContribInd(Dim.1))

As we’ve seen in the previous section, the students contributing the most to the first principal component have low grades, parents with low education, go out frequently with friends and a lot on average.

rmarkdown::paged_table(MostContribInd(Dim.2))

Students contributing the most to the second principal component either come from families with high social status (high parents education, lots of free time, etc.), or very low social status. It is also interesting to see that their guardian is often the mother and that most of these students intend to pursue higher education.

rmarkdown::paged_table(MostContribInd(Dim.3))

Students contributing the most to the third principal component have a lot of class absences and relatively low free time and weak family relationship. It is also interesting to see that most of them are females and that they don’t practice any extra-curricular activities.

rmarkdown::paged_table(MostContribInd(Dim.4))

Students contributing the most to the fourth principal component once again confirm our interpretation : they have in general a high number of class failures and don’t drink a lot, or vice versa. Moreover, we observe that most of them practice extra-curricular activities and that they come from big families.

rmarkdown::paged_table(MostContribInd(Dim.5))

Finally, students contributing the most to the fifth principal component are either older, have poor health but often go out with friends, or vice versa.

Conclusion on PCA

We chose to keep five principal components of our students survey dataset and tried to interpret them. However, these five principal components only explain approximately 57% of the variance in the data and their interpretations were relatively vague. Factor analysis could have lead to better interpretations, in particular using methods of loading rotation such as varimax. However, the lack of expert opinion on the right number of factors lead us to use PCA, so as to give a first interpretation of the possible latent factors and introduce the next method of analysis: multiple correspondence analysis.

MCA on categorical variables

As we’ve seen, this dataset contains mixed data, i.e. nominal, ordinal and numerical features. To apply MCA, we must pre-process the dataset by converting quantitative variables to ordinal (e.g. grades could be converted to “0-5”, “5-10”, etc.). The following variables will be converted:

The MCA model can then be fitted on the data.

# Create another dataset with quantitative variables converted to ordinals.
alcohol_cat <- alcohol %>%
  mutate(
    age = cut(
      age,
      breaks = c(-1, 18, 99), 
      labels = c("minor", "adult"),
    ),
    absences = cut(
      absences,
      breaks = c(-1, 10, 20, 30, 99),
      labels = c("<10", "10-20", "20-30", ">30")
    ),
    mean_grade = cut(
      mean_grade, 
      breaks = c(-1, 5, 10, 15, 20), 
      labels = c("0-5", "5-10" ,"10-15", "15-20")
    )
  ) %>%
  lapply(as.factor) %>%
  data.frame

# Perform MCA.
mca_res <- MCA(alcohol_cat, ncp = 5, graph = FALSE)
rotated_loadings_mca = varimax(mca_res$var$coord[, 1:5], normalize=TRUE)

fviz_screeplot(mca_res, addlabels = TRUE, ylim = c(0, 10))

As we were able to interpret five principal components using PCA, we choose to keep 5 factors in the MCA model. Varimax rotation will be applied to hopefully be able to give better interpretations of each factor, since the percentages of explained variances are relatively low.

Analysis of variables

plots <- list()
for (i in 1:5) {
  mca_cor_df <- data.frame(
    label = rownames(mca_res$var$contrib),
    loading = rotated_loadings_mca$loadings[, i]
  )
  plots[[i]] <- mca_cor_df %>% 
    ggplot(aes(x = label, y = loading)) +
      geom_bar(stat = "identity", fill = "steelblue") +
      coord_flip()
}

plots[[1]]

The first factor’s interpretation is straightforward: it correspond to low number of absences, between 0 and 5.

plots[[2]]

Similarly, the second factor corresponds to high consumption of alcohol, in particular during the working days.

plots[[3]]

The third factor corresponds to parents education. It is also interesting to see that high number of absences (more than 30) have a highly negative loading on this factor.

plots[[4]]

Categories such as the number of failures, high consumption of alcohol during the work days, being an adult or not having one’s parents as guardians have high loadings on the fourth factor. Thus, we could interpret this factor as representing adult students, possibly living by themselves, having relatively poor results in other classes.

plots[[5]]

The fifth and final factor we chose to interpret is a mixed bag. Overall alcohol consumption has a strong negative loading, while low father education has a positive loading. It could represent families with low social status, but a more precise interpretation might require more expert knowledge.

Conclusion on MCA

We chose to keep five principal components of our students survey dataset and tried to interpret them. However, these five principal components only explain approximately 57% of the variance in the data and their interpretations were relatively vague. Factor analysis could have lead to better interpretations, in particular using methods of loading rotation such as varimax. However, the lack of expert opinion on the right number of factors lead us to use PCA, so as to give a first interpretation of the possible latent factors and introduce the next method of analysis: multiple correspondence analysis.

LS0tCnRpdGxlOiAiRGF0YSBBbmFseXNpcyAtIEZpbmFsIFByb2plY3QiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgSW50cm9kdWN0aW9uCgpUaGUgZGF0YSB3ZXJlIG9idGFpbmVkIGluIGEgcG9ydHVndWVzZSBzdXJ2ZXkgb2Ygc3R1ZGVudCBlbnJvbGxlZCBpbiBhCnBvcnR1Z3Vlc2UgbGFuZ3VhZ2UgY291cnNlIGluIHNlY29uZGFyeSBzY2hvb2wuIFRoZSBmb2xsb3dpbmcgZmVhdHVyZXMgd2VyZQpyZWNvcmRlZDoKCjEuIGBzY2hvb2xgIC0gc3R1ZGVudCdzIHNjaG9vbCAoYmluYXJ5OiAnR1AnIC0gR2FicmllbCBQZXJlaXJhIG9yICdNUycgLSAKICAgTW91c2luaG8gZGEgU2lsdmVpcmEpCjIuIGBzZXhgIC0gc3R1ZGVudCdzIHNleCAoYmluYXJ5OiAnRicgLSBmZW1hbGUgb3IgJ00nIC0gbWFsZSkKMy4gYGFnZWAgLSBzdHVkZW50J3MgYWdlIChudW1lcmljOiBmcm9tIDE1IHRvIDIyKQo0LiBgYWRkcmVzc2AgLSBzdHVkZW50J3MgaG9tZSBhZGRyZXNzIHR5cGUgKGJpbmFyeTogJ1UnIC0gdXJiYW4gb3IgJ1InIC0gcnVyYWwpCjUuIGBmYW1zaXplYCAtIGZhbWlseSBzaXplIChiaW5hcnk6ICdMRTMnIC0gbGVzcyBvciBlcXVhbCB0byAzIG9yICdHVDMnIC0KICAgZ3JlYXRlciB0aGFuIDMpCjYuIGBQc3RhdHVzYCAtIHBhcmVudCdzIGNvaGFiaXRhdGlvbiBzdGF0dXMgKGJpbmFyeTogJ1QnIC0gbGl2aW5nIHRvZ2V0aGVyIG9yIAogICAnQScgLSBhcGFydCkKNy4gYMKgTWVkdWAgLSBtb3RoZXIncyBlZHVjYXRpb24gKG51bWVyaWM6IDAgLSBub25lLCAxIC0gcHJpbWFyeSBlZHVjYXRpb24gKDR0aAogICBncmFkZSksIDIg4oCTIDV0aCB0byA5dGggZ3JhZGUsIDMg4oCTIHNlY29uZGFyeSBlZHVjYXRpb24gb3IgNCDigJMgaGlnaGVyCiAgIGVkdWNhdGlvbikKOC4gYEZlZHVgIC0gZmF0aGVyJ3MgZWR1Y2F0aW9uIChudW1lcmljOiAwIC0gbm9uZSwgMSAtIHByaW1hcnkgZWR1Y2F0aW9uICg0dGgKICAgZ3JhZGUpLCAyIOKAkyA1dGggdG8gOXRoIGdyYWRlLCAzIOKAkyBzZWNvbmRhcnkgZWR1Y2F0aW9uIG9yIDQg4oCTIGhpZ2hlcgogICBlZHVjYXRpb24pCjkuIGBNam9iYCAtIG1vdGhlcidzIGpvYiAobm9taW5hbDogJ3RlYWNoZXInLCAnaGVhbHRoJyBjYXJlIHJlbGF0ZWQsIGNpdmlsCiAgICdzZXJ2aWNlcycgKGUuZy4gYWRtaW5pc3RyYXRpdmUgb3IgcG9saWNlKSwgJ2F0X2hvbWUnIG9yICdvdGhlcicpCjEwLiBgRmpvYmAgLSBmYXRoZXIncyBqb2IgKG5vbWluYWw6ICd0ZWFjaGVyJywgJ2hlYWx0aCcgY2FyZSByZWxhdGVkLCBjaXZpbAogICAgJ3NlcnZpY2VzJyAoZS5nLiBhZG1pbmlzdHJhdGl2ZSBvciBwb2xpY2UpLCAnYXRfaG9tZScgb3IgJ290aGVyJykKMTEuIGByZWFzb25gIC0gcmVhc29uIHRvIGNob29zZSB0aGlzIHNjaG9vbCAobm9taW5hbDogY2xvc2UgdG8gJ2hvbWUnLCBzY2hvb2wKICAgJ3JlcHV0YXRpb24nLCAnY291cnNlJyBwcmVmZXJlbmNlIG9yICdvdGhlcicpCjEyLiBgZ3VhcmRpYW5gIC0gc3R1ZGVudCdzIGd1YXJkaWFuIChub21pbmFsOiAnbW90aGVyJywgJ2ZhdGhlcicgb3IgJ290aGVyJykKMTMuIGB0cmF2ZWx0aW1lYCAtIGhvbWUgdG8gc2Nob29sIHRyYXZlbCB0aW1lIChudW1lcmljOiAxIC0gMSBob3VyKQoxNC4gYHN0dWR5dGltZWAgLSB3ZWVrbHkgc3R1ZHkgdGltZSAobnVtZXJpYzogMSAtIDEwIGhvdXJzKQoxNS4gYGZhaWx1cmVzYCAtIG51bWJlciBvZiBwYXN0IGNsYXNzIGZhaWx1cmVzIChudW1lcmljOiBuIGlmIDE8PW48MywgZWxzZSA0KQoxNi4gYHNjaG9vbHN1cGAgLSBleHRyYSBlZHVjYXRpb25hbCBzdXBwb3J0IChiaW5hcnk6IHllcyBvciBubykKMTcuIGBmYW1zdXBgIC0gZmFtaWx5IGVkdWNhdGlvbmFsIHN1cHBvcnQgKGJpbmFyeTogeWVzIG9yIG5vKQoxOC4gYHBhaWRgIC0gZXh0cmEgcGFpZCBjbGFzc2VzIHdpdGhpbiB0aGUgY291cnNlIHN1YmplY3QgKE1hdGggb3IgUG9ydHVndWVzZSkKICAgKGJpbmFyeTogeWVzIG9yIG5vKQoxOS4gYGFjdGl2aXRpZXNgIC0gZXh0cmEtY3VycmljdWxhciBhY3Rpdml0aWVzIChiaW5hcnk6IHllcyBvciBubykKMjAuIGBudXJzZXJ5YCAtIGF0dGVuZGVkIG51cnNlcnkgc2Nob29sIChiaW5hcnk6IHllcyBvciBubykKMjEuIGBoaWdoZXJgIC0gd2FudHMgdG8gdGFrZSBoaWdoZXIgZWR1Y2F0aW9uIChiaW5hcnk6IHllcyBvciBubykKMjIuIGBpbnRlcm5ldGAgLSBJbnRlcm5ldCBhY2Nlc3MgYXQgaG9tZSAoYmluYXJ5OiB5ZXMgb3Igbm8pCjIzLiBgcm9tYW50aWNgIC0gd2l0aCBhIHJvbWFudGljIHJlbGF0aW9uc2hpcCAoYmluYXJ5OiB5ZXMgb3Igbm8pCjI0LiBgZmFtcmVsYCAtIHF1YWxpdHkgb2YgZmFtaWx5IHJlbGF0aW9uc2hpcHMgKG51bWVyaWM6IGZyb20gMSAtIHZlcnkgYmFkIHRvCiAgIDUgLSBleGNlbGxlbnQpCjI1LiBgZnJlZXRpbWVgIC0gZnJlZSB0aW1lIGFmdGVyIHNjaG9vbCAobnVtZXJpYzogZnJvbSAxIC0gdmVyeSBsb3cgdG8gNSAtCiAgIHZlcnkgaGlnaCkKMjYuIGBnb291dGAgLSBnb2luZyBvdXQgd2l0aCBmcmllbmRzIChudW1lcmljOiBmcm9tIDEgLSB2ZXJ5IGxvdyB0byA1IC0gdmVyeQogICBoaWdoKQoyNy4gYERhbGNgIC0gd29ya2RheSBhbGNvaG9sIGNvbnN1bXB0aW9uIChudW1lcmljOiBmcm9tIDEgLSB2ZXJ5IGxvdyB0byA1IC0KICAgdmVyeSBoaWdoKQoyOC4gYFdhbGNgIC0gd2Vla2VuZCBhbGNvaG9sIGNvbnN1bXB0aW9uIChudW1lcmljOiBmcm9tIDEgLSB2ZXJ5IGxvdyB0byA1IC0KICAgdmVyeSBoaWdoKQoyOS4gYGhlYWx0aGAgLSBjdXJyZW50IGhlYWx0aCBzdGF0dXMgKG51bWVyaWM6IGZyb20gMSAtIHZlcnkgYmFkIHRvIDUgLSB2ZXJ5CiAgIGdvb2QpCjMwLiBgYWJzZW5jZXNgIC0gbnVtYmVyIG9mIHNjaG9vbCBhYnNlbmNlcyAobnVtZXJpYzogZnJvbSAwIHRvIDkzKQoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoZmFzdER1bW1pZXMpCmxpYnJhcnkoRmFjdG9NaW5lUikKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShwc3ljaCkKCiMgTG9hZCBkYXRhc2V0LgphbGNvaG9sIDwtIHJlYWQuY3N2KCJzdHVkZW50LXBvci5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gVFJVRSkKcm1hcmtkb3duOjpwYWdlZF90YWJsZShhbGNvaG9sKQpgYGAKCiMgQ29ycmVsYXRpb24gbWF0cml4CgpXZSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgZmVhdHVyZXMuIFRvIGRvIHRoaXMsCnZhcmlhYmxlcyBtdXN0IGJlIGR1bW15IGNvZGVkLCBpLmUuIG5ldyBiaW5hcnkgdmFyaWFibGVzIGFyZSBjcmVhdGVkIHRvIGVuY29kZQpub21pbmFsIHZhcmlhYmxlcy4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgRHVtbXkgY29kZSB2YXJpYWJsZXMuCmFsY29ob2xfZHVtbXlfY29kZWQgPC0gZHVtbXlfY29sdW1ucygKICBhbGNvaG9sLAogIHJlbW92ZV9maXJzdF9kdW1teSA9IFRSVUUsCiAgcmVtb3ZlX3NlbGVjdGVkX2NvbHVtbnMgPSBUUlVFCikKYGBgCgpUaGUgY29ycmVsYXRpb24gbWF0cml4IGNhbiB0aGVuIGJlIGNvbXB1dGVkLgoKYGBge3IgZmlnMX0KIyBQbG90IGNvcnJlbGF0aW9uIG1hdHJpeC4KYWxjb2hvbF9kdW1teV9jb2RlZCAlPiUgCiAgY29yICU+JQogIG1lbHQgJT4lCiAgZ2dwbG90KGFlcyh4ID0gVmFyMSwgeSA9IFZhcjIsIGZpbGwgPSB2YWx1ZSkpICsgCiAgICBnZW9tX3RpbGUoY29sb3IgPSAiZ3JleSIpICsgCiAgICBzY2FsZV9maWxsX2dyYWRpZW50MigKICAgICAgbG93ID0gImJsdWUiLCBoaWdoID0gInJlZCIsIG1pZCA9ICJ3aGl0ZSIsCiAgICAgIG1pZHBvaW50ID0gMCwgbGltaXQgPSBjKC0xLDEpLCBuYW1lID0gIlBlYXJzb25cbkNvcnJlbGF0aW9uIgogICAgKSArIAogICAgdGhlbWUoCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA1LCBoanVzdCA9IDEsIHZqdXN0ID0gMCksCiAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1LCBoanVzdCA9IDEsIHZqdXN0ID0gMCksCiAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksIAogICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNSkKICAgICkgKwogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9jb2xvdXJiYXIoYmFyd2lkdGggPSAwLjUsIGJhcmhlaWdodCA9IDUpKSArCiAgICBjb29yZF9maXhlZCgpICsKICAgIHhsYWIoIiIpICsKICAgIHlsYWIoIiIpIApgYGAKClVuc3VycHJpc2luZ2x5LCBncmFkZXMgYEcxYCwgYEcyYCBhbmQgYEczYCBhcmUgc3Ryb25nbHkgcG9zaXRpdmVseSBjb3JyZWxhdGVkLgpTaW5jZSBpbmNsdWRpbmcgbmVhcmx5LXJlZHVuZGFudCB2YXJpYWJsZXMgY2FuIGNhdXNlIG1ldGhvZHMgbGlrZSBQQ0EgdG8Kb3ZlcmVtcGhhc2l6ZSB0aGVpciBjb250cmlidXRpb24gKGkuZS4gc2V2ZXJhbCBlaWdlbnZlY3RvcnMgd2lsbCBiZSBtb3JlCndlaWdodGVkLCB0aGVyZWJ5IGNoYW5naW5nIHRoZSBkaXJlY3Rpb25zIG9mIGFsbCBlaWdlbnZlY3RvcnMpLCB3ZSBjaG9vc2UgdG8Ka2VlcCBvbmx5IHRoZSBtZWFuIG9mIHRoZXNlIGdyYWRlcy4KCmBgYHtyfQojIENvbXB1dGUgbWVhbiBvZiBncmFkZXMgRzEsIEcyIGFuZCBHMy4KYWxjb2hvbCA8LSBhbGNvaG9sICU+JQogIG11dGF0ZShtZWFuX2dyYWRlID0gKEcxICsgRzIgKyBHMykvMykgJT4lCiAgc2VsZWN0KC1jKEcxLCBHMiwgRzMpKQpgYGAKCiMgUENBIG9uIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMKClNpbmNlIHdlIGRvbid0IGhhdmUgYWNjZXNzIHRvIGFuIGV4cGVydCBvcGluaW9uIG9uIHRoaXMgZGF0YXNldCwgd2UgY2Fubm90CmRldGVybWluZSB0aGUgbnVtYmVyIG9mIGZhY3RvcnMgaW4gYWR2YW5jZS4gVGh1cywgaXQgbWFrZXMgbW9yZSBzZW5zZSB0byB1c2UKcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyBvdmVyIGZhY3RvciBhbmFseXNpcy4gUENBIGNhbiBiZSBhcHBsaWVkIG9uIGFsbApudW1lcmljIGFuZCBvcmRpbmFsIHZhcmlhYmxlcyBpZiB0aGVyZSBpcyBzdWZmaWNpZW50IGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlbQooUENBIG9uIGEgc2V0IG9mIG9ydGhvZ29uYWwgdmFyaWFibGVzIHdvdWxkIGxlYWQgdG8gdGhlIHNhbWUgc2V0IG9mIHZhcmlhYmxlcykuClNpbmNlIHRoZSBwcmV2aW91cyBjb3JyZWxhdGlvbiBtYXRyaXggY2xlYXJseSBpc24ndCB0aGUgaWRlbnRpdHkgbWF0cml4Cihjb25jZXJuZWQgcmVhZGVycyBjb3VsZCBydW4gYSBCYXJ0bGV0dCB0ZXN0IG9mIGhvbW9nZW5laXR5IG9mIHZhcmlhbmNlcyB0byBiZQphc3N1cmVkIG9mIHRoaXMpLCBQQ0EgY2FuIGJlIGFwcGxpZWQuCgpgYGB7ciBmaWcyfQphbGNvaG9sX251bWVyaWNfc2NhbGVkIDwtIGFsY29ob2wgJT4lCiAgc2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JQogIHNjYWxlCgpwY2FfcmVzIDwtIFBDQShhbGNvaG9sX251bWVyaWNfc2NhbGVkLCAKICAgICAgICAgICAgICAgbmNwID0gMTAsIAogICAgICAgICAgICAgICBzY2FsZS51bml0ID0gRkFMU0UsCiAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCgpmdml6X2VpZyhwY2FfcmVzKQpgYGAKClRoZSBwcmV2aW91cyBzY3JlZXBsb3Qgc2hvd3MgdGhhdCB3ZSBjb3VsZG4ndCBmaW5kIHZlcnkgc3Ryb25nIGNvbXBvbmVudHMuClNpbmNlIHRoZXJlIGlzbid0IGFueSBzaGFycCBicmVhayBpbiB0aGUgc2NyZWVwbG90LCB3ZSB1c2UgSG9ybidzIHBhcmFsbGVsCmFuYWx5c2lzIHRvIGRldGVybWluZSB0aGUgbnVtYmVyIG9mIGNvbXBvbmVudHMgdG8ga2VlcC4KCmBgYHtyIGZpZzN9CmZhLnBhcmFsbGVsKGFsY29ob2wgJT4lIHNlbGVjdF9pZihpcy5udW1lcmljKSwgZmEgPSAicGMiKQpgYGAKCiMjIEFuYWx5c2lzIG9mIHZhcmlhYmxlcwoKTGV0J3MgbG9vayBhdCB0aGUgZmlyc3QgZml2ZSBwcmluY2lwYWwgY29tcG9uZW50cyBhbmQgdHJ5IHRvIGludGVycHJldCB0aGVtIGluCnJlbGF0aW9uIHRvIHRoZSBzdXJ2ZXkgdmFyaWFibGVzOgoKYGBge3IgZmlnNH0KcGxvdHMgPC0gbGlzdCgpCmZvciAoaSBpbiAxOjUpIHsKICBwY2FfY29yX2RmIDwtIGRhdGEuZnJhbWUoCiAgICBsYWJlbCA9IGFsY29ob2wgJT4lIHNlbGVjdF9pZihpcy5udW1lcmljKSAlPiUgbmFtZXMsCiAgICBjb3JyZWxhdGlvbiA9IHBjYV9yZXMkdmFyJGNvclssIGldCiAgKQogIHBsb3RzW1tpXV0gPC0gcGNhX2Nvcl9kZiAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBsYWJlbCwgeSA9IGNvcnJlbGF0aW9uKSkgKwogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJzdGVlbGJsdWUiKSArCiAgICAgIGNvb3JkX2ZsaXAoKSArIAogICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKQp9CgpwbG90c1tbMV1dCmBgYAoKU2VlaW5nIHRoYXQgdGhlIGZpcnN0IHByaW5jaXBhbCBjb21wb25lbnQgc2hvd3MgaGlnaCBwb3NpdGl2ZSBjb3JyZWxhdGlvbiB3aXRoCmZlYXR1cmVzIGxpa2UgYWxjb2hvbCBjb25zdW1wdGlvbiwgbnVtYmVyIG9mIGNsYXNzIGZhaWx1cmVzIGFuZCBmcmVxdWVuY3kgb2YKdGltZSBzcGVudCBvdXQgd2l0aCBmcmllbmQgYW5kIGhpZ2ggbmVnYXRpdmUgY29ycmVsYXRpb24gd2l0aCB0aGUgbWVhbiBncmFkZSwKdGltZSBzcGVudCBzdHVkeWluZyBhbmQgcGFyZW50cycgZWR1Y2F0aW9uLCBpdCBjb3VsZCByZXByZXNlbnQgdGhlICoqc2Nob29sIApmYWlsdXJlcyBjYXVzZWQgYnkgYWxjb2hvbCBjb25zdW1wdGlvbiwgbGFjayBvZiBzdHVkeWluZyBhbmQvb3IgcGFyZW50J3MgbGFjawpvZiBlZHVjYXRpb24qKi4KCmBgYHtyIGZpZzV9CnBsb3RzW1syXV0KYGBgCgpUaGUgc2Vjb25kIGNvbXBvbmVudCBpcyBoaWdobHkgY29ycmVsYXRlZCB3aXRoIHRoZSBwYXJlbnRzJyBlZHVjYXRpb24gYW5kCnJlbGF0aXZlbHkgY29ycmVsYXRlZCB3aXRoIGFsY29ob2wgY29uc3VtcHRpb24gYW5kIHRpbWUgc3BlbnQgb3V0IHdpdGggZnJpZW5kLAp3aGlsZSBiZWluZyBzbGlnaHRseSBhbnRpLWNvcnJlbGF0ZWQgd2l0aCB0cmF2ZWwgdGltZS4gSXQgY291bGQgcmVwcmVzZW50CnRoZSAqKnNvY2lhbCBzdGF0dXMqKiwgc2luY2Ugc3R1ZGVudHMgd2hvc2UgcGFyZW50cyByZWNlaXZlZCBiZXR0ZXIgZWR1Y2F0aW9uCmFyZSBtb3JlIGxpa2VseSB0byBiZWxvbmcgdG8gaGlnaGVyIHNvY2lhbCBjbGFzc2VzLgoKYGBge3IgZmlnNn0KcGxvdHNbWzNdXQpgYGAKClRoZSB0aGlyZCBjb21wb25lbnQgaXMgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCBmcmVlIHRpbWUgYW5kIGZhbWlseSByZWxhdGlvbnNoaXAKYW5kIGFudGktY29ycmVsYXRlZCB3aXRoIHRoZSBzdHVkZW50J3MgYWJzZW5jZXMuIEl0IGNvdWxkIHJlcHJlc2VudCB0aGUKc3R1ZGVudCdzICoqZmFtaWx5IHJlbGF0aW9uc2hpcCoqLCBhcyBzdHVkZW50cyB3aG8gaGF2ZSBnb29kIGZhbWlseQpyZWxhdGlvbnNoaXAgYXJlIGxpa2VseSB0byBiZSBnaXZlbiBtb3JlIGZyZWUgdGltZSBhbmQgbm90IHNraXAgc2Nob29sLgoKYGBge3IgZmlnN30KcGxvdHNbWzRdXQpgYGAKClRoZSBmb3VydGggY29tcG9uZW50IGNvdWxkIHJlcHJlc2VudCB0aGUgKipjbGFzcyBmYWlsdXJlcyB0aGF0IGFyZW4ndCBjYXVzZWQgYnkKYWxjb2hvbCBjb25zdW1wdGlvbiBvciBsYWNrIG9mIHN0dWR5aW5nKiosIGFzIG9wcG9zZWQgdG8gdGhlIGZpcnN0IGNvbXBvbmVudC4KSG93ZXZlciwgdGhlIG51bWVyaWNhbCBhbmQgb3JkaW5hbCBkYXRhIGRvZXNuJ3QgY29udGFpbiB2YXJpYWJsZXMgdGhhdCBleHBsYWluCnRoZXNlIGZhaWx1cmVzLgoKYGBge3IgZmlnOH0KcGxvdHNbWzVdXQpgYGAKClRoZSBmaWZ0aCBjb21wb25lbnQgaXMgY29ycmVsYXRlZCB3aXRoIGFnZSBhbmQgc3R1ZHkgdGltZSBhbmQgYW50aS1jb3JyZWxhdGVkCndpdGggaGVhbHRoLiBJdCBpcyBoYXJkZXIgdG8gcmVwcmVzZW50LCBidXQgY291bGQgcm91Z2hseSByZXByZXNlbnQgKipvbGRlcgpzdHVkZW50cyB3aG8gbWlnaHQgYmUgZXhoYXVzdGVkIGZyb20gc3R1ZHlpbmcsIG9yIG1pZ2h0IG5vdCBiZSBoYXBweSB3aXRoIHRoZWlyCmVudmlyb25tZW50IGR1ZSB0byB0aGVpciBvbGRlciBhZ2UqKi4KCiMjIEFuYWx5c2lzIG9mIGluZGl2aWR1YWxzCgpUbyBkZWVwZW4gb3VyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIHByZXZpb3VzbHkgc3R1ZGllZCBjb21wb25lbnRzLCBsZXQncyBsb29rCmF0IHRoZSBtb3N0IGNvbnRyaWJ1dGluZyBpbmRpdmlkdWFscyBvZiBlYWNoIGNvbXBvbmVudC4KCmBgYHtyfQojIEZpbmQgdGhlIHRlbiBtb3N0IGNvbnRyaWJ1dGluZyBpbmRpdmlkdWFscyB0byBQQyBuLgpNb3N0Q29udHJpYkluZCA8LSBmdW5jdGlvbihkaW0sIG4gPSAxMCkgewogIGRpbV9uYW1lIDwtIGVuZXhwcihkaW0pCgogIGluZCA8LSBwY2FfcmVzJGluZCRjb250cmliICU+JSAKICAgIGRhdGEuZnJhbWUgJT4lIAogICAgc2VsZWN0KCEhZGltX25hbWUpICU+JSAKICAgIGFycmFuZ2UoZGVzYyghIWRpbV9uYW1lKSkgJT4lIAogICAgaGVhZChuID0gbikgJT4lCiAgICByb3duYW1lcyAlPiUKICAgIGFzLm51bWVyaWMKCiAgcmV0dXJuKGFsY29ob2xbaW5kLCBdKQp9CgpybWFya2Rvd246OnBhZ2VkX3RhYmxlKE1vc3RDb250cmliSW5kKERpbS4xKSkKYGBgCgpBcyB3ZSd2ZSBzZWVuIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uLCB0aGUgc3R1ZGVudHMgY29udHJpYnV0aW5nIHRoZSBtb3N0IHRvCnRoZSBmaXJzdCBwcmluY2lwYWwgY29tcG9uZW50IGhhdmUgbG93IGdyYWRlcywgcGFyZW50cyB3aXRoIGxvdyBlZHVjYXRpb24sIGdvCm91dCBmcmVxdWVudGx5IHdpdGggZnJpZW5kcyBhbmQgYSBsb3Qgb24gYXZlcmFnZS4KCmBgYHtyfQpybWFya2Rvd246OnBhZ2VkX3RhYmxlKE1vc3RDb250cmliSW5kKERpbS4yKSkKYGBgCgpTdHVkZW50cyBjb250cmlidXRpbmcgdGhlIG1vc3QgdG8gdGhlIHNlY29uZCBwcmluY2lwYWwgY29tcG9uZW50IGVpdGhlciBjb21lCmZyb20gZmFtaWxpZXMgd2l0aCBoaWdoIHNvY2lhbCBzdGF0dXMgKGhpZ2ggcGFyZW50cyBlZHVjYXRpb24sIGxvdHMgb2YgZnJlZQp0aW1lLCBldGMuKSwgb3IgdmVyeSBsb3cgc29jaWFsIHN0YXR1cy4gSXQgaXMgYWxzbyBpbnRlcmVzdGluZyB0byBzZWUgdGhhdAp0aGVpciBndWFyZGlhbiBpcyBvZnRlbiB0aGUgbW90aGVyIGFuZCB0aGF0IG1vc3Qgb2YgdGhlc2Ugc3R1ZGVudHMgaW50ZW5kIHRvCnB1cnN1ZSBoaWdoZXIgZWR1Y2F0aW9uLgoKYGBge3J9CnJtYXJrZG93bjo6cGFnZWRfdGFibGUoTW9zdENvbnRyaWJJbmQoRGltLjMpKQpgYGAKClN0dWRlbnRzIGNvbnRyaWJ1dGluZyB0aGUgbW9zdCB0byB0aGUgdGhpcmQgcHJpbmNpcGFsIGNvbXBvbmVudCBoYXZlIGEgbG90IG9mCmNsYXNzIGFic2VuY2VzIGFuZCByZWxhdGl2ZWx5IGxvdyBmcmVlIHRpbWUgYW5kIHdlYWsgZmFtaWx5IHJlbGF0aW9uc2hpcC4gSXQgaXMKYWxzbyBpbnRlcmVzdGluZyB0byBzZWUgdGhhdCBtb3N0IG9mIHRoZW0gYXJlIGZlbWFsZXMgYW5kIHRoYXQgdGhleSBkb24ndApwcmFjdGljZSBhbnkgZXh0cmEtY3VycmljdWxhciBhY3Rpdml0aWVzLgoKYGBge3J9CnJtYXJrZG93bjo6cGFnZWRfdGFibGUoTW9zdENvbnRyaWJJbmQoRGltLjQpKQpgYGAKClN0dWRlbnRzIGNvbnRyaWJ1dGluZyB0aGUgbW9zdCB0byB0aGUgZm91cnRoIHByaW5jaXBhbCBjb21wb25lbnQgb25jZSBhZ2Fpbgpjb25maXJtIG91ciBpbnRlcnByZXRhdGlvbiA6IHRoZXkgaGF2ZSBpbiBnZW5lcmFsIGEgaGlnaCBudW1iZXIgb2YgY2xhc3MKZmFpbHVyZXMgYW5kIGRvbid0IGRyaW5rIGEgbG90LCBvciAqdmljZSB2ZXJzYSouIE1vcmVvdmVyLCB3ZSBvYnNlcnZlIHRoYXQgbW9zdApvZiB0aGVtIHByYWN0aWNlIGV4dHJhLWN1cnJpY3VsYXIgYWN0aXZpdGllcyBhbmQgdGhhdCB0aGV5IGNvbWUgZnJvbSBiaWcKZmFtaWxpZXMuCgpgYGB7cn0Kcm1hcmtkb3duOjpwYWdlZF90YWJsZShNb3N0Q29udHJpYkluZChEaW0uNSkpCmBgYAoKRmluYWxseSwgc3R1ZGVudHMgY29udHJpYnV0aW5nIHRoZSBtb3N0IHRvIHRoZSBmaWZ0aCBwcmluY2lwYWwgY29tcG9uZW50IGFyZQplaXRoZXIgb2xkZXIsIGhhdmUgcG9vciBoZWFsdGggYnV0IG9mdGVuIGdvIG91dCB3aXRoIGZyaWVuZHMsIG9yICp2aWNlIHZlcnNhKi4KCiMjIENvbmNsdXNpb24gb24gUENBCgpXZSBjaG9zZSB0byBrZWVwIGZpdmUgcHJpbmNpcGFsIGNvbXBvbmVudHMgb2Ygb3VyIHN0dWRlbnRzIHN1cnZleSBkYXRhc2V0IGFuZAp0cmllZCB0byBpbnRlcnByZXQgdGhlbS4gSG93ZXZlciwgdGhlc2UgZml2ZSBwcmluY2lwYWwgY29tcG9uZW50cyBvbmx5IGV4cGxhaW4KYXBwcm94aW1hdGVseSA1NyUgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkYXRhIGFuZCB0aGVpciBpbnRlcnByZXRhdGlvbnMgd2VyZQpyZWxhdGl2ZWx5IHZhZ3VlLiBGYWN0b3IgYW5hbHlzaXMgY291bGQgaGF2ZSBsZWFkIHRvIGJldHRlciBpbnRlcnByZXRhdGlvbnMsCmluIHBhcnRpY3VsYXIgdXNpbmcgbWV0aG9kcyBvZiBsb2FkaW5nIHJvdGF0aW9uIHN1Y2ggYXMgdmFyaW1heC4gSG93ZXZlciwgdGhlCmxhY2sgb2YgZXhwZXJ0IG9waW5pb24gb24gdGhlIHJpZ2h0IG51bWJlciBvZiBmYWN0b3JzIGxlYWQgdXMgdG8gdXNlIFBDQSwgc28KYXMgdG8gZ2l2ZSBhIGZpcnN0IGludGVycHJldGF0aW9uIG9mIHRoZSBwb3NzaWJsZSBsYXRlbnQgZmFjdG9ycyBhbmQgaW50cm9kdWNlCnRoZSBuZXh0IG1ldGhvZCBvZiBhbmFseXNpczogbXVsdGlwbGUgY29ycmVzcG9uZGVuY2UgYW5hbHlzaXMuCgojIE1DQSBvbiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKCkFzIHdlJ3ZlIHNlZW4sIHRoaXMgZGF0YXNldCBjb250YWlucyBtaXhlZCBkYXRhLCBpLmUuIG5vbWluYWwsIG9yZGluYWwgYW5kCm51bWVyaWNhbCBmZWF0dXJlcy4gVG8gYXBwbHkgTUNBLCB3ZSBtdXN0IHByZS1wcm9jZXNzIHRoZSBkYXRhc2V0IGJ5IGNvbnZlcnRpbmcKcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyB0byBvcmRpbmFsIChlLmcuIGdyYWRlcyBjb3VsZCBiZSBjb252ZXJ0ZWQgdG8gIjAtNSIsCiI1LTEwIiwgZXRjLikuIFRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIHdpbGwgYmUgY29udmVydGVkOgoKLSBgYWdlYDogIm1pbm9yIiwgImFkdWx0Ii4KLSBgYWJzZW5jZXNgOiAiMC0xMCIsICIxMC0yMCIsICIyMC0zMCIsICI+MzAiOwotIGBtZWFuX2dyYWRlYGA6ICIwLTUiLCAiNS0xMCIsICIxMC0xNSIsICIxNS0yMCIuCgpUaGUgTUNBIG1vZGVsIGNhbiB0aGVuIGJlIGZpdHRlZCBvbiB0aGUgZGF0YS4KCmBgYHtyfQojIENyZWF0ZSBhbm90aGVyIGRhdGFzZXQgd2l0aCBxdWFudGl0YXRpdmUgdmFyaWFibGVzIGNvbnZlcnRlZCB0byBvcmRpbmFscy4KYWxjb2hvbF9jYXQgPC0gYWxjb2hvbCAlPiUKICBtdXRhdGUoCiAgICBhZ2UgPSBjdXQoCiAgICAgIGFnZSwKICAgICAgYnJlYWtzID0gYygtMSwgMTgsIDk5KSwgCiAgICAgIGxhYmVscyA9IGMoIm1pbm9yIiwgImFkdWx0IiksCiAgICApLAogICAgYWJzZW5jZXMgPSBjdXQoCiAgICAgIGFic2VuY2VzLAogICAgICBicmVha3MgPSBjKC0xLCAxMCwgMjAsIDMwLCA5OSksCiAgICAgIGxhYmVscyA9IGMoIjwxMCIsICIxMC0yMCIsICIyMC0zMCIsICI+MzAiKQogICAgKSwKICAgIG1lYW5fZ3JhZGUgPSBjdXQoCiAgICAgIG1lYW5fZ3JhZGUsIAogICAgICBicmVha3MgPSBjKC0xLCA1LCAxMCwgMTUsIDIwKSwgCiAgICAgIGxhYmVscyA9IGMoIjAtNSIsICI1LTEwIiAsIjEwLTE1IiwgIjE1LTIwIikKICAgICkKICApICU+JQogIGxhcHBseShhcy5mYWN0b3IpICU+JQogIGRhdGEuZnJhbWUKCiMgUGVyZm9ybSBNQ0EuCm1jYV9yZXMgPC0gTUNBKGFsY29ob2xfY2F0LCBuY3AgPSA1LCBncmFwaCA9IEZBTFNFKQpyb3RhdGVkX2xvYWRpbmdzX21jYSA9IHZhcmltYXgobWNhX3JlcyR2YXIkY29vcmRbLCAxOjVdLCBub3JtYWxpemU9VFJVRSkKCmZ2aXpfc2NyZWVwbG90KG1jYV9yZXMsIGFkZGxhYmVscyA9IFRSVUUsIHlsaW0gPSBjKDAsIDEwKSkKYGBgCgpBcyB3ZSB3ZXJlIGFibGUgdG8gaW50ZXJwcmV0IGZpdmUgcHJpbmNpcGFsIGNvbXBvbmVudHMgdXNpbmcgUENBLCB3ZSBjaG9vc2UgdG8Ka2VlcCA1IGZhY3RvcnMgaW4gdGhlIE1DQSBtb2RlbC4gVmFyaW1heCByb3RhdGlvbiB3aWxsIGJlIGFwcGxpZWQgdG8gaG9wZWZ1bGx5CmJlIGFibGUgdG8gZ2l2ZSBiZXR0ZXIgaW50ZXJwcmV0YXRpb25zIG9mIGVhY2ggZmFjdG9yLCBzaW5jZSB0aGUgcGVyY2VudGFnZXMgb2YKZXhwbGFpbmVkIHZhcmlhbmNlcyBhcmUgcmVsYXRpdmVseSBsb3cuCgojIyBBbmFseXNpcyBvZiB2YXJpYWJsZXMKCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTN9CnBsb3RzIDwtIGxpc3QoKQpmb3IgKGkgaW4gMTo1KSB7CiAgbWNhX2Nvcl9kZiA8LSBkYXRhLmZyYW1lKAogICAgbGFiZWwgPSByb3duYW1lcyhtY2FfcmVzJHZhciRjb250cmliKSwKICAgIGxvYWRpbmcgPSByb3RhdGVkX2xvYWRpbmdzX21jYSRsb2FkaW5nc1ssIGldCiAgKQogIHBsb3RzW1tpXV0gPC0gbWNhX2Nvcl9kZiAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBsYWJlbCwgeSA9IGxvYWRpbmcpKSArCiAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInN0ZWVsYmx1ZSIpICsKICAgICAgY29vcmRfZmxpcCgpCn0KCnBsb3RzW1sxXV0KYGBgCgpUaGUgZmlyc3QgZmFjdG9yJ3MgaW50ZXJwcmV0YXRpb24gaXMgc3RyYWlnaHRmb3J3YXJkOiBpdCBjb3JyZXNwb25kIHRvICoqbG93Cm51bWJlciBvZiBhYnNlbmNlcywgYmV0d2VlbiAwIGFuZCA1KiouCgpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0zfQpwbG90c1tbMl1dCmBgYAoKU2ltaWxhcmx5LCB0aGUgc2Vjb25kIGZhY3RvciBjb3JyZXNwb25kcyB0byAqKmhpZ2ggY29uc3VtcHRpb24gb2YgYWxjb2hvbCwgaW4KcGFydGljdWxhciBkdXJpbmcgdGhlIHdvcmtpbmcgZGF5cyoqLgoKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9M30KcGxvdHNbWzNdXQpgYGAKClRoZSB0aGlyZCBmYWN0b3IgY29ycmVzcG9uZHMgdG8gKipwYXJlbnRzIGVkdWNhdGlvbioqLiBJdCBpcyBhbHNvIGludGVyZXN0aW5nCnRvIHNlZSB0aGF0IGhpZ2ggbnVtYmVyIG9mIGFic2VuY2VzIChtb3JlIHRoYW4gMzApIGhhdmUgYSBoaWdobHkgbmVnYXRpdmUKbG9hZGluZyBvbiB0aGlzIGZhY3Rvci4KCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTN9CnBsb3RzW1s0XV0KYGBgCgpDYXRlZ29yaWVzIHN1Y2ggYXMgdGhlIG51bWJlciBvZiBmYWlsdXJlcywgaGlnaCBjb25zdW1wdGlvbiBvZiBhbGNvaG9sIGR1cmluZwp0aGUgd29yayBkYXlzLCBiZWluZyBhbiBhZHVsdCBvciBub3QgaGF2aW5nIG9uZSdzIHBhcmVudHMgYXMgZ3VhcmRpYW5zIGhhdmUKaGlnaCBsb2FkaW5ncyBvbiB0aGUgZm91cnRoIGZhY3Rvci4gVGh1cywgd2UgY291bGQgaW50ZXJwcmV0IHRoaXMgZmFjdG9yCmFzIHJlcHJlc2VudGluZyAqKmFkdWx0IHN0dWRlbnRzLCBwb3NzaWJseSBsaXZpbmcgYnkgdGhlbXNlbHZlcywgaGF2aW5nCnJlbGF0aXZlbHkgcG9vciByZXN1bHRzIGluIG90aGVyIGNsYXNzZXMqKi4KCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTN9CnBsb3RzW1s1XV0KYGBgCgpUaGUgZmlmdGggYW5kIGZpbmFsIGZhY3RvciB3ZSBjaG9zZSB0byBpbnRlcnByZXQgaXMgYSBtaXhlZCBiYWcuIE92ZXJhbGwKYWxjb2hvbCBjb25zdW1wdGlvbiBoYXMgYSBzdHJvbmcgbmVnYXRpdmUgbG9hZGluZywgd2hpbGUgbG93IGZhdGhlciBlZHVjYXRpb24KaGFzIGEgcG9zaXRpdmUgbG9hZGluZy4gSXQgY291bGQgcmVwcmVzZW50ICoqZmFtaWxpZXMgd2l0aCBsb3cgc29jaWFsIHN0YXR1cyoqLApidXQgYSBtb3JlIHByZWNpc2UgaW50ZXJwcmV0YXRpb24gbWlnaHQgcmVxdWlyZSBtb3JlIGV4cGVydCBrbm93bGVkZ2UuCgojIyBDb25jbHVzaW9uIG9uIE1DQQoKCgpXZSBjaG9zZSB0byBrZWVwIGZpdmUgcHJpbmNpcGFsIGNvbXBvbmVudHMgb2Ygb3VyIHN0dWRlbnRzIHN1cnZleSBkYXRhc2V0IGFuZAp0cmllZCB0byBpbnRlcnByZXQgdGhlbS4gSG93ZXZlciwgdGhlc2UgZml2ZSBwcmluY2lwYWwgY29tcG9uZW50cyBvbmx5IGV4cGxhaW4KYXBwcm94aW1hdGVseSA1NyUgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkYXRhIGFuZCB0aGVpciBpbnRlcnByZXRhdGlvbnMgd2VyZQpyZWxhdGl2ZWx5IHZhZ3VlLiBGYWN0b3IgYW5hbHlzaXMgY291bGQgaGF2ZSBsZWFkIHRvIGJldHRlciBpbnRlcnByZXRhdGlvbnMsCmluIHBhcnRpY3VsYXIgdXNpbmcgbWV0aG9kcyBvZiBsb2FkaW5nIHJvdGF0aW9uIHN1Y2ggYXMgdmFyaW1heC4gSG93ZXZlciwgdGhlCmxhY2sgb2YgZXhwZXJ0IG9waW5pb24gb24gdGhlIHJpZ2h0IG51bWJlciBvZiBmYWN0b3JzIGxlYWQgdXMgdG8gdXNlIFBDQSwgc28KYXMgdG8gZ2l2ZSBhIGZpcnN0IGludGVycHJldGF0aW9uIG9mIHRoZSBwb3NzaWJsZSBsYXRlbnQgZmFjdG9ycyBhbmQgaW50cm9kdWNlCnRoZSBuZXh0IG1ldGhvZCBvZiBhbmFseXNpczogbXVsdGlwbGUgY29ycmVzcG9uZGVuY2UgYW5hbHlzaXMu